#ifndef INFO_H
#define INFO_H

//*****************************************************************包含头文件*****************************************************************

//C / C++
#include <stdio.h>
#include <string.h>
#include <wchar.h>
#include <locale.h>
#include <dirent.h>
#include <math.h>
#include <sstream>

//QT...................
#include <QWidget>
#include <QPushButton>
#include <QString>
#include <QComboBox>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QTextEdit>
#include <QAction>
#include <QInputDialog>
#include <QDateTime>
#include <QFileDialog>
#include <QFile>
#include <QMessageBox>
#include <QTimer>
#include <QTimeEdit>
#include <QMainWindow>
#include <QStyle>
#include <QDebug>
#include <QDir>
#include <QPainter>
#include "qtextcodec.h"
#include <QDialog>
#include <QTableWidget>
#include <QTableWidgetItem>
#include <QProcess>
#include <QRandomGenerator>

//*****************************************************************定义结构体 宏 常量 等等*****************************************************************

#define TIMENAME_MAX    30                      //时间字符串长
#define NAMELENMAX      30                      //名字长
#define TYPELENMAX      30                      //类型长
#define IDLENMAX        30                      //ID长
#define PWSLENMAX       30                      //密码长
#define MAJLENMAX       30                      //专业名长
#define MAJINDEXMAX     100                     //专业个数上限
#define CLASSINDEXMAX   100                     //班级编号上限
#define YEARINDEXMAX    100                     //年级编号上限
#define ALLCLASSMAX     1000                    //最多几个班
#define SCOREMAX        1000                    //单套题分数上限
#define SMALLNOMAX      4000                    //单套题小题上限

#define FILENAME_NUMMAX         8               //宏定义了几个文件名

#define FILENAME_SETTING        "setting.csv"   //程序配置文件名
#define FILENAME_USERINFO       "users"         //默认用户信息文件夹路径
#define FILENAME_PROBLEM        "problem"       //默认题目合集文件夹路径
#define FILENAME_STUDENTS       "students.csv"  //学生信息表
#define FILENAME_TEACHERS       "teachers.csv"  //教师信息表
#define FILENAME_STATISTICS     "statistic.csv" //统计表
#define FILENAME_KEY            "keys.csv"      //密码表
#define FILENAME_RECORDS        "records"       //记录文件夹
#define FILENAME_MAJORS         "majors.csv"    //编码 -- 专业表


#define LOGIN_NTRYMAX           5               //登录尝试次数上限
#define LOGIN_RETRYTIMEOUT      toSeconds(0,0,5,0)//异常重试时间上限

#define STU_REC_SPILT           "|"             //记录文件名所用的分隔符

#define REC_QSTID_LMAX          10              //小题号文本长度

#define STU_TABLE_SHOWROW       9               //一页8行



//懒人专属宏定义。。。。。。
#define ENV_SET_CN setlocale(LC_ALL,NULL)	//设置中文环境

#define ENV_SET_QT QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8")) //Qt处理char*的方式

#define GETWSTRMB(dest,ansi) {size_t m;mbstowcs_s(&m, dest, strlen(ansi)+1,ansi, _TRUNCATE);dest[m-1] = 0;}
                                    //多字节字符串转化为宽字符串 记得加'\0'

#define GETMBSTR(dest,s)    {size_t m;wcstombs_s(&m, dest, wcslen(s)+1,s, _TRUNCATE);dest[m-1] = 0;}
                                    //宽字符串转化为多字节字符串

#define GETQSTRMB(dest,s)   {QTextCodec* cc = QTextCodec::codecForLocale();dest = cc->toUnicode(s);}
                                    //多字节字符串转为QString

#define GETMBQSTR(dest,s)   {auto p = (s).toLocal8Bit();strcpy(dest,p.data());}
                                    //QString转为多字节字符串

#define GETQSTRWS(mbbufsz,dest,wstr) {char p1[mbbufsz] = {0};GETMBSTR((p1),(wstr));GETQSTRMB((dest),(p1));}
                                    //宽字符串转化为QString

#define GETWSQSTR(mbbufsz,dest,qstr) {char p1[mbbufsz] = {0};GETMBQSTR((p1),(qstr));GETWSTRMB((dest),(p1));}
                                    //QString转化为宽字符串

#define FNAMECAT(buffer,f1,f2,sp) {strcpy(buffer,f1);strcat(buffer,sp);strcat(buffer,f2);}
                                    //f1+sp+f2把文件路径用连起来

//学号,姓名,类型,登录失败次数,密码,最近登录时间(时间戳)
#define GETARGS_KEYSW(line,bufID,bufName,bufType,bufNtry,bufPws,bufTime) \
    wchar_t bufID[IDLENMAX]={0},bufName[NAMELENMAX]={0},bufType[TYPELENMAX]={0},bufNtry[IDLENMAX]={0},bufPws[PWSLENMAX]={0},bufTime[TIMENAME_MAX]={0};                                                  \
    swscanf(line,L"%[^,],%[^,],%[^,],%[^,],%[^,],%[^\n]",bufID,bufName,bufType,bufNtry,bufPws,bufTime);
                                    //得到一行的密码表参数 wchar_t版

#define PRINTARGS_KEYSA(line,bufID,bufName,bufType,bufNtry,bufPws,bufTime) \
    sprintf(line,"%lld,%s,%s,%d,%s,%lld\n",bufID,bufName,bufType,bufNtry,bufPws,bufTime);

#define PRINTARGS_KEYSW(line,bufID,bufName,bufType,bufNtry,bufPws,bufTime) \
    swprintf(line,L"%lld,%ls,%ls,%d,%ls,%lld\n",bufID,bufName,bufType,bufNtry,bufPws,bufTime);


//学号,姓名,专业,年级,班级,作答记录（点击超链接查看）,总正确率（%）
#define PRINTARGS_STUSA(line,reciv) \
    sprintf(line,"%lld,%s,%s,%d,%d,%s,%d\n",reciv.ID,reciv.name,reciv.major,reciv.year,reciv.cls)

#define PRINTARGS_STUSAFILE(fp,reciv) \
    fprintf(fp,"%lld,%s,%s,%d,%d,%s,%d\n",reciv.ID,reciv.name,reciv.major,reciv.year,reciv.cls)

//访问配置窗体
#define ASKFORSETTING(fst,parent) \
    form_setting fst(parent);\
    fst.setModal(1);\
    fst.set = parent->set;\
    fst.update_ini();\
    fst.show();\
    fst.exec();\


//把qt控件的可用性翻转
#define QTSWITCHENABLED(ctrl) ctrl->setEnabled(!ctrl->isEnabled())


//带合法检查性的学生赋值语句
#define STU_ASSIGN_ID(stud,aft,alert) \
    stud->ID = fix_number((aft),1,INT64_MAX,alert);

#define STU_ASSIGN_YEAR(stud,aft,alert) \
    stud->year = fix_number((aft),0,YEARINDEXMAX,alert);

#define STU_ASSIGN_CLASS(stud,aft,alert) \
    stud->cls = fix_number((aft),1,CLASSINDEXMAX,alert);

#define STU_ASSIGN_GRADE(stud,aft,alert) \
    stud->grd = fix_number((aft),0,100,alert);

//======================================================================================类型定义
/*
 *
 * 数据标准 在Qt外
 * 时间           所有的时间都使用时间戳 long long
 *                除特别标注   _时间 如_sec代表秒为单位
 *
 * 文本           所有涉及到中文的文本读写 均使用wchar_t存储 UTF-8编码
 *                路径统一用'/'分隔
 * 编码标准: UTF-8
 *
 * 结构总览                             说明
 * 学生                                   在程序内 以双向链表形式储存
 *      学号
        专业编号(动态分配)                 将专业名通过散列变为整数编号 节约空间
        入学年份
        班级编号
        姓名
        提交记录文件链表(动态分配)          同样是以链表形式存储 链表存储的是文件路径
        总分
 *
 *
*/


//==================================================================================程序配置
/*setting.csv:
  0     [用户信息文件夹路径 = 默认 ./users]
  1     [题目合集文件夹路径 = 默认 ./problems]
*/
struct setting{
    wchar_t userfd[FILENAME_MAX];
    wchar_t problemfd[FILENAME_MAX];
};


//==================================================================================用户
//用户类型：学生，教师，管理员，不能辨认
typedef enum {
    Student = 0,Teacher = 1,Admin = 2,not_identified = 3,guest = 4
}usertype;


//用户信息
struct userInfo{
    usertype    type;                       //用户类型
    wchar_t     username[NAMELENMAX];       //用户名
    long long   id;                         //ID
    wchar_t     pws[PWSLENMAX];             //密码
    long        ofs_keys;                   //其记录在keys.csv的位置 方便再次查询
};

//登录信息————每尝试登录一次都会出现
struct loginInfo{
    userInfo    user;                       //用户信息
    long long   time;                       //登录时间戳
    int         ntry;                       //尝试登录次数 登录成功清0
    int         bSuc;                       //是否成功登录
};


//==============泛型节点结构体定义
struct node {
    void*       con = 0;                    //内容指针 需要强制转换使用
    int         type;                       //数据域类型
    node*       last = 0;                   //上一个指针
    node*       next = 0;                   //下一个指针
};


//==============学生信息结构体定义
struct student {
    long long   ID;                         //学号
    char        major[2*MAJLENMAX];         //专业名
    int         year;                       //入学年份
    int         cls;                        //班级编号
    char        name[2*NAMELENMAX];         //姓名
    node        *recfiles = 0;              //提交记录文件链表头节点(动态分配)
    int         grd;                        //总分
};

//==============记录信息结构体定义
struct record {
    long long   stuID;                      //提交学生学号
    wchar_t     problemname[FILENAME_MAX];  //提交题目文件
    double      score;                      //得分
    long long   ans_time;                   //作答时间
    long long   used_time_sec;              //用时时间(s)
    node*       specific = 0;               //具体情况记录链表头节点(动态分配)
};

//==============具体小题记录信息结构体定义
struct specificRec {
    wchar_t     qstID[REC_QSTID_LMAX];      //小题号文本
    double      fullscore;                  //满分
    double      actualscore;                //实际得分


};


enum nodetype{                              //存放不同数据类型的大小
    stu         =   sizeof(student),
    rec         =   sizeof(record),
    spe         =   sizeof(specificRec),
    filename    =   FILENAME_MAX            //文件路径
};


enum OperationLineFile{Delete,Replace,Insert};  //editlinefile操作类型

typedef std::vector<std::string> stringVec;

//==================================================================================文件管理
//==
//编码标准: locale = GBK, Qt = UTF-8
//==
/*默认情况下：（未列出只写文件）
 *程序.exe
 *setting.csv           --> 程序配置文件
 *用户信息(文件夹)       <-- 可自定义
 *        学生信息表.csv --> 访问学生信息，提交记录列表，总成绩
 *        教师信息表.csv --> 访问学生信息，管理班级等
 *        统计数据表.csv --> 全体学生，各班，各专业等等的统计数据
 *        密码表.csv     --> 用户密码表
 *        专业表.csv     --> 动态根据专业生成的辅助对应表
 *        提交记录(文件夹)
 *            提交学生ID+时间.csv
 *            xxxxx312300444420231220155624.csv --> 存储提交信息
 *            ...
 *
 *题目合集(文件夹)       <-- 可自定义 题目的存放路径
 *        一组题可以放在一个文件夹中 也可以单独放
 *        2020年试卷(文件夹)
 *            题目1.html      -->打开做题
 *            题目2.html
 *            ...
 *        2021年试卷(文件夹)
 *            题目1.html
 *            题目2.html
 *        test1.html
 *        test2.html
 *        ...
*/



/*students.csv
 *  学号,姓名,专业,年级,班级,作答记录（点击超链接查看）,总正确率（%）
 *  ...
 *  332211,guest_student,计算机类,23,13,试试|ssd|时间:/dds/d/a/s,43
 *
 *
 *
*/



/*keys.csv 主要用于登录
 *      学号,用户名,类型,登录失败次数,密码,最近登录时间(时间戳)
 * ..              学生
 * ..              教师
 *
 *
*/

//记录格式
//文件名 题目名学生ID+时间.csv
//第0行是表头
//提交学生学号,提交题目文件,得分,作答时间,用时
//第2行也是表头
//题号("（1）"),题目分数(double),实际得分(double),"学生答案","正确答案",状态


//***************************************************************************************************************************************************************************************************
//***************************************************************************************************************************************************************************************************
//*****************************************************************声明全局函数***********************************************************************************************************************
//***************************************************************************************************************************************************************************************************
//***************************************************************************************************************************************************************************************************


//====================================================================================================================================================================Qt窗体函数声明：

//登录                         见form_welcome.h
//做题器/题目编辑器             见form_qst.h
//主窗体                       见form_main.h


//====================================================================================================================================================================辅助

long long   getCurrentTick();                                                                                   //获得当前时间戳
long long   toSeconds(int d,int h,int m,int s);                                                                 //天时分秒-->秒

int save_file_string(QString fname,QString context);                                                            //把Qt字符串内容写入fname文件
double fix_number(double n,double min,double max,int alert);                                                    //将数值限制在一定范围 并可指定是否弹出警告

void        setTextBoxLine_tree(QTreeWidget* pt,QTreeWidgetItem* head);                                         //输入行节点 设置树的所有列节点为textbox
int         openAble(char* filename);                                                                           //文件是否可以打开
int         createEmptyFile(char* filename);                                                                    //创建一个空文件
long long   sizeFile(FILE* ret);                                                                                //返回文件大小
long long   sizeFile(char* filename);
void        getUserFilename(setting *set,char* name,char* res);                                                 //获取用户文件路径
void        openUserFile_system(setting *set,char* name);                                                       //用(WINDOWS)系统方式打开文件
FILE*       openUserFile(setting *set,char* name,char* mode);                                                   //打开用户文件

usertype    getTypeFromStr(QString qstr);                                                                       //从字符串中得到用户类型
usertype    getTypeFromStr(wchar_t* wstr);
QString     getStrFromType(usertype t);                                                                         //从用户类型中得到字符串
void        getStrFromType(usertype t,char* v);
FILE*       editLineFile(char* filename,long start,int row,char* mbstr,OperationLineFile mode);                 //对文件某行进行插入/删除/替换 返回临时文件
FILE*       editLineFileW(char* filename,int row,wchar_t* wcstr,OperationLineFile mode);                        //宽字符版
long long   copyFile_ptr(char* dest,FILE* fp);                                                                  //按文件指针复制/创建到dest路径
long long   copyFile_path(char* dest,char* src);                                                                //把路径src的内容复制给dest
void        sprintfarg_keyA(char* line,loginInfo* lginf);                                                       //返回keys.csv的一行多字节字符


int         stu_majupdate(char* majname);                                                                       //根据
std::string getStrFromRecL(node* head);                                                                         //从用户记录文件链表中转为打印的字符串
QString     getStrFromStu(student* stud);                                                                       //把学生转为打印的字符串

void        stdStrSplit(const std::string& src,char delim,std::vector<std::string>& res);                       //把string分割开来
long long   hashStr(char* target,long long max,int k);                                                          //散列函数

//====================================================================================================================================================================设置

void    getSettingDefault(setting* set);                                                                    //获得默认配置
int     getSettingInfo(setting* set);                                                                       //从配置文件读取配置
void    saveSettingInfo(setting* set);                                                                      //保存配置文件
int     checkSettingInfo(setting* set);                                                                     //查看文件完整性
void    applySetting(setting* set);                                                                         //配置程序


//====================================================================================================================================================================链表

//API   对于传出的新链表 统一要用二级指针参数 设0代表不需要
node*   createEmptyNode();                                                                                  //创建空类型节点 标记首尾
node*   createNewNode(int type);                                                                            //创建特定类型节点
void    freeNode(node* target);                                                                             //释放节点
node*   advanceNode(node* target,int k,int (*stop)(node*),int* sp);                                         //移动节点k个单位 可正可负 可指定停止条件
void    copyNode(node* dest,node* src);                                                                     //复制节点内容

void    list_delete(node* target);                                                                          //删除链表节点 不包括头尾节点
void    list_clear(node* head);                                                                             //给定头节点 销毁链表
void    list_headtail(node** head,node** tail);                                                             //得到一个空链表的头尾
node*   list_push_back(int type,node* tail);                                                                //向尾部追加
node*   list_push_front(int type,node* head);                                                               //向头部追加
node*   list_insert_back(int type,node* head);                                                              //新建节点插入head之后
node*   list_insert_front(int type,node* tail);                                                             //新建节点插入tail之前

node*   list_at(node* head,int i);                                                                          //返回head开始i个单位
void    list_sort(node* head,node* tail,int (*pfunc)(node* a, node* b),node** newhead,node** newtail);      //原址排序 返回头尾
node*   list_find_next(node* head,int (*stop)(node*),int* sp);                                              //查找head之后第一个指定条件的节点
void    list_find_list(node* head,int (*stop)(node*),node** list_result);                                   //查找指定条件的所有节点 形成新链表
void    list_cat(node* head1,node* head2,node** result_list);                                               //链表头尾拼接
void    list_cpy(node* head,node** result_list);                                                            //链表复制

int     cmp_student_ID_less(node* a,node* b);                                                               //排序函数例
int     cmp_student_ID_greater(node* a,node* b);

//强制转换便民宏定义
#define NODE_STUDENT_UNDEF(node) ((student*)(node->con))
#define NODE_STUDENT(dest,node) student* dest = node ? (student*)(node->con) : 0                            //强制转换为学生类型
#define NODE_FILENAME_UNDEF(node) ((char*)(node->con))
#define NODE_FILENAME(dest,node) char* dest = node ? (char*)(node->con) : 0                                 //强制转换为char字符串指针


//内部使用
node*   _DelEmptyHT(node* head,node* tail);                                                                 //将空头尾删除 替换为 0
node*   _AddEmptyHT(node* head,node* tail);                                                                 //在头尾加空节点
node*   _list_sort(node* L,int (*pfunc)(node* a, node* b),node** tail);                                     //无头尾的归并排序

//排序函数和转移表 0为升序 1为降序 第二维是学生表列数
int liststu_ID_u(node* a,node* b);
int liststu_cls_u(node* a,node* b);
int liststu_grd_u(node* a,node* b);
int liststu_year_u(node* a,node* b);
int liststu_name_u(node* a,node* b);
int liststu_major_u(node* a,node* b);
int liststu_ID_d(node* a,node* b);
int liststu_cls_d(node* a,node* b);
int liststu_grd_d(node* a,node* b);
int liststu_year_d(node* a,node* b);
int liststu_name_d(node* a,node* b);
int liststu_major_d(node* a,node* b);
#endif // INFO_H
